/**
 * Copyright (c) 2022 Presto Labs Pte. Ltd.
 * Author: Ju
 */

#pragma once

#include <boost/asio/connect.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <cstdint>
#include <memory>
#include <string>

#include "coin2/base/memory/mem_slice.h"

namespace coin2::base::tcp {
using boost::asio::ip::tcp;
using namespace coin2::base::memory;

class TcpConnection : public std::enable_shared_from_this<TcpConnection> {
 public:
  class Callback {
   public:
    virtual ~Callback() = default;
    virtual void OnConnect(std::weak_ptr<TcpConnection> conn, bool is_connect) {
      (void)conn;
      (void)is_connect;
    };
    virtual void OnError(std::weak_ptr<TcpConnection> conn,
                         boost::system::error_code ec) = 0;
    virtual void OnRawDataReceived(std::weak_ptr<TcpConnection> conn,
                                   memory::MemSlice buffer, uint32_t len) = 0;

    virtual void OnDataSent(uint32_t len) { (void)len; };
  };

  explicit TcpConnection(boost::asio::io_context *ioc);
  ~TcpConnection();

  void Start();
  void Close();
  void SendMsg(std::string buffer);
  void SendMsg(memory::MemSlice buffer);

  void Connect(std::string ip, uint16_t port);
  void Connect(tcp::endpoint ep);

  tcp::socket &Socket();

  bool IsConnect();

  // remote address
  std::string Ip();
  uint16_t Port();
  void RegisterCallback(Callback *callback);

 private:
  static const uint32_t MAX_SIZE = 4096;

 private:
  boost::asio::io_context *ioc_;
  boost::asio::io_service::strand strand_;
  tcp::socket socket_;
  Callback *callback_;

  bool is_connect_{false};
  std::vector<char> buffer_;
};

inline tcp::socket &TcpConnection::Socket() { return socket_; }

inline std::string TcpConnection::Ip() {
  return socket_.remote_endpoint().address().to_string();
}

inline uint16_t TcpConnection::Port() {
  return socket_.remote_endpoint().port();
}

inline void TcpConnection::RegisterCallback(Callback *callback) {
  callback_ = callback;
}

inline bool TcpConnection::IsConnect() { return is_connect_; }


}  // namespace coin2::base::tcp
